﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using NGinnBPM.MessageBus;
using System.Timers;
using NLog;

namespace NGinnBPM.Services.Utilities
{
    /// <summary>
    /// Simple cache class
    /// </summary>
    public class TimedCache : IDisposable
    {
        private class CacheItem
        {
            public object Value;
            public DateTime Expires;
        }

        private Hashtable _items = new Hashtable();
        public TimeSpan DefaultExpirationTime { get; set; }
        private Timer _tm;
        private int _hits = 1;
        private int _gets = 1;
        private string _name = "TimedCache";

        private Logger log = LogManager.GetCurrentClassLogger();

        public TimedCache()
        {
            DefaultExpirationTime = TimeSpan.FromMinutes(2);
            _tm = new Timer();
            _tm.Elapsed += PeriodicCleanup;
            _tm.AutoReset = true;
            _tm.Interval = 10000;
            _tm.Start();
        }

        public string Name
        {
            get { return _name; }
            set { _name = value; log = LogManager.GetLogger("TimedCache_" + _name); }
        }


        public object Get(string key)
        {
            lock (this)
            {
                _gets++;
                CacheItem ci = (CacheItem)_items[key];
                if (ci != null) _hits++;
                return ci == null ? null : ci.Value;
            }
        }

        public void Put(string key, object obj)
        {
            PutTimed(key, obj, DateTime.Now.Add(DefaultExpirationTime));
        }

        public void PutTimed(string key, object obj, DateTime expires)
        {
            CacheItem ci = new CacheItem();
            ci.Value = obj;
            ci.Expires = expires;
            lock (this)
            {
                _items[key] = ci;
            }
        }

        public void Invalidate(string key)
        {
            lock (this)
            {
                _items.Remove(key);
            }
        }

        public void InvalidateAll(string key)
        {
            lock (this)
            {
                _items = new Hashtable();
            }
        }

        private void PeriodicCleanup(object sender, ElapsedEventArgs e)
        {
            lock (this)
            {
                ArrayList al = new ArrayList();
                foreach(string k in _items.Keys)
                {
                    CacheItem ci = (CacheItem) _items[k];
                    if (ci.Expires <= DateTime.Now)
                    {
                        al.Add(k);
                    }
                }
                if (al.Count > 0)
                    log.Debug("Cache {1}: Expiring {0} cache entries", al.Count, this.Name);
                foreach (string k in al)
                    _items.Remove(k);
            }
            if (_items.Count > 0)
                log.Debug("Cache stats: {0}, size: {1}", ((double)_hits) / _gets, _items.Count);
        }

        #region IDisposable Members

        public void Dispose()
        {
            lock (this)
            {
                if (_tm != null)
                    _tm.Dispose();
                _tm = null;
            }
        }

        #endregion
    }
}
